﻿using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Xml;
using System.Xml.Linq;

namespace DynamicGeometry
{
    public abstract class FigureBase : IFigure
    {
        public string c_store_id { get; set; }
        public string c_guid { get; set; }

        public string c_type { get; set; }
        public bool c_is_modify { get; set; }

        public FigureBase()
        {
            ID = ++nextID;
            Name = this.GetType().Name + ID;
            Exists = true;
            Visible = true;
        }

        public override string ToString()
        {
            return Name;
        }

        [PropertyGridVisible]
        public string Name { get; set; }

        public virtual bool Selected { get; set; }

        [PropertyGridVisible]
        public virtual bool Visible { get; set; }

        public virtual void WriteXml(XmlWriter writer)
        {

        }

        public virtual void ReadXml(XElement element)
        {

        }

        [PropertyGridVisible]
        public virtual void Delete()
        {
            var allDependents = this.AsEnumerable<IFigure>()
                .TopologicalSort(f => f.Dependents);
            
            using (var transaction = Transaction.Create(Drawing.ActionManager, false))
            {
                foreach (var figure in allDependents)
                {
                    RemoveFigureAction action = new RemoveFigureAction(Drawing, figure);
                    Drawing.ActionManager.RecordAction(action);
                }
            }

            Drawing.RaiseSelectionChanged(new Drawing.SelectionChangedEventArgs());
        }

        private static int nextID = 0;
        public static int ID { get; set; }

        protected Canvas Canvas 
        { 
            get 
            { 
                return Drawing.Canvas; 
            } 
        }
        
        protected PointPair CanvasLogicalBorders 
        { 
            get 
            {
                return ToLogical(Canvas.GetBorderRectangle());
            } 
        }

        private IEnumerable<IFigure> mDependencies;
        public IEnumerable<IFigure> Dependencies
        {
            get
            {
                //if (mDependencies == null)
                //{
                //    mDependencies = Enumerable.Empty<IFigure>();
                //}
                return mDependencies;
            }
            set
            {
                mDependencies = value;
            }
        }

        private IFigureList mDependents;
        public IFigureList Dependents
        {
            get
            {
                if (mDependents == null)
                {
                    mDependents = new FigureList();
                }
                return mDependents;
            }
            protected set
            {
                mDependents = value;
            }
        }

        public virtual int ZIndex { get; set; }
        
        public virtual bool Exists { get; set; }

        public Point Point(int index)
        {
            return (Dependencies.ElementAt(index) as IPoint).Coordinates;
        }

        public PointPair Line(int index)
        {
            return (Dependencies.ElementAt(index) as ILine).Coordinates;
        }

        public double Number(int index)
        {
            return (Dependencies.ElementAt(index) as INumber).Value;
        }

        public virtual void OnAddingToCanvas(Canvas newContainer)
        {
            Canvas.SetZIndex(newContainer, this.ZIndex);
        }

        public virtual void OnRemovingFromCanvas(Canvas leavingContainer)
        {
        }

        public Drawing Drawing { get; set; }

        public virtual void UpdateExistence()
        {
            if (!Dependencies.IsEmpty())
            {
                Exists = Dependencies.All(f => f.Exists);
            }
        }


        public virtual void Recalculate() { }

        /// <summary>
        /// Takes Coordinates or whatever other location information is current for the figure
        /// and updates the shape or other visual representation with these coordinates
        /// </summary>
        /// <example>
        /// Usually means updating the Shape like this:
        /// Shape.MoveTo(Coordinates.ToPhysical());
        /// </example>
        public virtual void UpdateVisual() { }

        public abstract IFigure HitTest(Point point);

        public bool Equals(IFigure other)
        {
            return object.ReferenceEquals(this, other);
        }

        #region Coordinates

        protected double CursorTolerance
        {
            get
            {
                return Drawing.CoordinateSystem.CursorTolerance;
            }
        }

        protected double ToPhysical(double logicalLength)
        {
            return Drawing.CoordinateSystem.ToPhysical(logicalLength);
        }

        protected Point ToPhysical(Point point)
        {
            return Drawing.CoordinateSystem.ToPhysical(point);
        }

        protected PointPair ToPhysical(PointPair pointPair)
        {
            return Drawing.CoordinateSystem.ToPhysical(pointPair);
        }

        protected double ToLogical(double pixelLength)
        {
            return Drawing.CoordinateSystem.ToLogical(pixelLength);
        }

        protected Point ToLogical(Point pixel)
        {
            return Drawing.CoordinateSystem.ToLogical(pixel);
        }

        protected PointPair ToLogical(PointPair pointPair)
        {
            return Drawing.CoordinateSystem.ToLogical(pointPair);
        }

        #endregion
    }
}